/*
 * Copyright (c) 2010, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "alkes/gfxfilter/GfxFilterGraphics.h"
#include "alkes/gfxfilter/GfxFilterRenderTarget.h"
#include "alkes/gfxfilter/GfxFilterVertexBuffer.h"
#include "alkes/gfxfilter/GfxFilterDecoderFactory.h"
#include "alkes/gfxfilter/GfxFilterImageBufferFactory.h"

namespace alkes {

#define IS_ENABLE_FILTER(classname, method) \
    this->isEnableLogFilter(AL_FILTER_##classname##method)

GfxFilterGraphics* GfxFilterGraphics::create(IGraphics3D* component)
{
    if (!component) return 0;

    return new GfxFilterGraphics(component);
}

void GfxFilterGraphics::destroy()
{
    delete this;
}

GfxFilterGraphics::GfxFilterGraphics(IGraphics3D* component)
: component_(component)
{
    enableAllLogFilter();
}

GfxFilterGraphics::~GfxFilterGraphics()
{
}

void GfxFilterGraphics::resize(uint16_t width, uint16_t height)
{
    if (IS_ENABLE_FILTER(IGraphics, resize))
    {
        AL_LOG_INVOKED(IGraphics, resize, "%d,%d", width, height);
    }
    component_->resize(width, height);
}

void GfxFilterGraphics::flip()
{
    if (IS_ENABLE_FILTER(IGraphics, flip))
    {
        AL_LOG_INVOKED(IGraphics, flip, "");
    }
    component_->flip();
}

IRenderTarget2D* GfxFilterGraphics::createDefaultRenderTarget2D()
{
    if (IS_ENABLE_FILTER(IGraphics2D, createDefaultRenderTarget2D))
    {
        AL_LOG_INVOKED(IGraphics2D, createDefaultRenderTarget2D, "");
    }

    IRenderTarget2D* target(component_->createDefaultRenderTarget2D());

    if (!target)
        return 0;

    GfxFilterRenderTarget* ret = GfxFilterRenderTarget::createRenderTarget2D(this, target);

    if (IS_ENABLE_FILTER(IGraphics2D, createDefaultRenderTarget2D))
    {
        AL_LOG_RETVAL(IRenderTarget2D*, "0x%p", AL_REAL_PTR(ret, GfxFilterRenderTarget));
    }

    return ret;
}

IRenderTarget3D* GfxFilterGraphics::createDefaultRenderTarget3D()
{
    if (IS_ENABLE_FILTER(IGraphics3D, createDefaultRenderTarget3D))
    {
        AL_LOG_INVOKED(IGraphics3D, createDefaultRenderTarget3D, "");
    }

    IRenderTarget3D* target(component_->createDefaultRenderTarget3D());

    if (!target)
        return 0;

    GfxFilterRenderTarget* ret = GfxFilterRenderTarget::createRenderTarget3D(this, target);

    if (IS_ENABLE_FILTER(IGraphics3D, createDefaultRenderTarget3D))
    {
        AL_LOG_RETVAL(IRenderTarget3D*, "0x%p", AL_REAL_PTR(ret, GfxFilterRenderTarget));
    }

    return ret;
}

IVertexBuffer* GfxFilterGraphics::createVertexBuffer(uint32_t vertex_type)
{
    if (IS_ENABLE_FILTER(IGraphics3D, createVertexBuffer))
    {
        AL_LOG_INVOKED(IGraphics3D, createVertexBuffer, "0x%08x", vertex_type);
    }

    intrusive_ptr<IVertexBuffer> vbuffer(component_->createVertexBuffer(vertex_type));

    if (!vbuffer)
        return 0;

    GfxFilterVertexBuffer* ret = GfxFilterVertexBuffer::createVertexBuffer(vbuffer);

    if (IS_ENABLE_FILTER(IGraphics3D, createVertexBuffer))
    {
        AL_LOG_RETVAL(IVertexBuffer*, "0x%p", AL_REAL_PTR(ret, GfxFilterVertexBuffer));
    }

    return ret;
}

DecoderFactory* GfxFilterGraphics::createDecoderFactory()
{
    if (IS_ENABLE_FILTER(IGraphics2D, createDecoderFactory))
    {
        AL_LOG_INVOKED(IGraphics2D, createDecoderFactory, "");
    }

    DecoderFactory* ret(component_->createDecoderFactory());

    if (IS_ENABLE_FILTER(IGraphics2D, createDecoderFactory))
    {
        AL_LOG_RETVAL(DecoderFactory*, "0x%p", AL_REAL_PTR(ret, GfxFilterDecoderFactory));
    }

    return ret;
}

ImageBufferFactory* GfxFilterGraphics::createImageBufferFactory()
{
    if (IS_ENABLE_FILTER(IGraphics2D, createImageBufferFactory))
    {
        AL_LOG_INVOKED(IGraphics2D, createImageBufferFactory, "");
    }

    ImageBufferFactory* ret(component_->createImageBufferFactory());

    if (IS_ENABLE_FILTER(IGraphics2D, createImageBufferFactory))
    {
        AL_LOG_RETVAL(ImageBufferFactory*, "0x%p", AL_REAL_PTR(ret, GfxFilterImageBufferFactory));
    }

    return ret;
}

Shape* GfxFilterGraphics::createShape(int32_t shape_type, uint32_t num)
{
    if (IS_ENABLE_FILTER(IGraphics2D, createShape))
    {
        AL_LOG_INVOKED(IGraphics2D, createShape, "(ShapeType)%d, %d", shape_type, num);
    }

    Shape* ret(component_->createShape(shape_type, num));

    if (IS_ENABLE_FILTER(IGraphics2D, createShape))
    {
        AL_LOG_RETVAL(Shape*, "0x%p", ret);
    }

    return ret;
}

LineShape* GfxFilterGraphics::createLineShape(uint32_t num)
{
    if (IS_ENABLE_FILTER(IGraphics2D, createLineShape))
    {
        AL_LOG_INVOKED(IGraphics2D, createLineShape, "%d", num);
    }

    LineShape* ret(component_->createLineShape(num));

    if (IS_ENABLE_FILTER(IGraphics2D, createLineShape))
    {
        AL_LOG_RETVAL(LineShape*, "0x%p", ret);
    }

    return ret;
}

RectangleShape* GfxFilterGraphics::createRectangleShape(uint32_t num)
{
    if (IS_ENABLE_FILTER(IGraphics2D, createRectangleShape))
    {
        AL_LOG_INVOKED(IGraphics2D, createRectangleShape, "%d", num);
    }

    RectangleShape* ret(component_->createRectangleShape(num));

    if (IS_ENABLE_FILTER(IGraphics2D, createRectangleShape))
    {
        AL_LOG_RETVAL(RectangleShape*, "0x%p", ret);
    }

    return ret;
}

ImageShape* GfxFilterGraphics::createImageShape(uint32_t num)
{
    if (IS_ENABLE_FILTER(IGraphics2D, createImageShape))
    {
        AL_LOG_INVOKED(IGraphics2D, createImageShape, "%d", num);
    }

    ImageShape* ret(component_->createImageShape(num));

    if (IS_ENABLE_FILTER(IGraphics2D, createImageShape))
    {
        AL_LOG_RETVAL(ImageShape*, "0x%p", ret);
    }

    return ret;
}

PolylineShape* GfxFilterGraphics::createPolylineShape(uint32_t num)
{
    if (IS_ENABLE_FILTER(IGraphics2D, createPolylineShape))
    {
        AL_LOG_INVOKED(IGraphics2D, createPolylineShape, "%d", num);
    }

    PolylineShape* ret(component_->createPolylineShape(num));

    if (IS_ENABLE_FILTER(IGraphics2D, createPolylineShape))
    {
        AL_LOG_RETVAL(PolylineShape*, "0x%p", ret);
    }

    return ret;
}


#define MAKE_BIT(v) ((uint32_t)((uint32_t)1 << ((v)%32)))
#define INDEX(v)    ((v) / 32)
#define ARRAY_LEN(a)    sizeof(a)/sizeof(a[0])

void GfxFilterGraphics::enableAllLogFilter()
{
    int i;
    for (i = 0; i < static_cast<int>(ARRAY_LEN(log_filter_)); ++i)
    {
        log_filter_[i] = 0xffffffff;
    }
}

void GfxFilterGraphics::disableAllLogFilter()
{
    int i;
    for (i = 0; i < static_cast<int>(ARRAY_LEN(log_filter_)); ++i)
    {
        log_filter_[i] = 0;
    }
}

void GfxFilterGraphics::enableLogFilter(LogFilter filter)
{
    log_filter_[INDEX(filter)] |= MAKE_BIT(filter);
}

void GfxFilterGraphics::disableLogFilter(LogFilter filter)
{
    log_filter_[INDEX(filter)] &= ~MAKE_BIT(filter);
}

bool GfxFilterGraphics::isEnableLogFilter(LogFilter filter) const
{
   return 0 != (log_filter_[INDEX(filter)] & MAKE_BIT(filter));
}

}
